<!--
 * @Autor: ZhangYao
 * @Date: 2022-11-16 15:45:09
 * @LastEditors: ZhangYao
 * @LastEditTime: 2023-02-27 09:53:28
-->
<template>
  <div class="work-wrapper">
    <div class="top-wrapper">
      <div class="top-left">
        <i class="el-icon-document"></i>
        <span class="title">{{ state.targetInfo ? state.targetInfo.name : "" }}</span>
        <el-tag>{{
          state.targetInfo.taskScheduleType == "realtime" ? "实时" : "周期"
        }}</el-tag>
      </div>
      <div class="top-right">
        <el-button @click="execute" type="primary">{{state.targetInfo.state == '1' ? '停止' : '启动'}}</el-button>
        <el-button @click="checkLogHandle">查看日志</el-button>
        <el-button @click="executeOnce">执行一次</el-button>
      </div>
    </div>
    <div class="content-wrapper">
      <div class="flink-type-wrapper">
        <div class="flink-type-item" v-for="item in state.flinkType">
          <span>{{ item.label }}</span>
        </div>
      </div>
      <div class="graph-wrapper">
        <div class="zoom-wrapper">
          <el-button
            data-command="zoomOut"
            class="u-button"
            title="缩小"
            icon="el-icon-zoom-out"
            @click="commandHandler('zoomOut')"
          />
          <el-button
            class="u-button"
            data-command="zoomIn"
            title="放大"
            icon="el-icon-zoom-in"
            @click="commandHandler('zoomIn')"
          />
        </div>

        <div class="outline" ref="outlineRef"></div>
        <div class="graph" id="graph"></div>
      </div>

      <div class="func-wrapper">
        <div class="func-btn" @click="detailHandle">任务详情</div>
        <div
          class="func-btn"
          v-if="state.targetInfo.taskScheduleType == 'batch'"
          @click="dispatchConfigHandle"
        >
          调度配置
        </div>
        <div class="func-btn" v-if="state.targetInfo.xxlJobId" @click="dependencyConfigHandle">依赖配置</div>
        <div class="func-btn" v-if="state.targetInfo.xxlJobId" @click="dependencyViewHandle">依赖视图</div>
      </div>
    </div>
    <SqlEditor ref="sqlEditorRef" />
    <Jar ref="jarRef" />
    <Studio ref="studioRef" />
    <!-- <el-dialog
      title="调度配置"
      :visible="dispatchConfigVisible"
      :before-close="
        () => {
          dispatchConfigVisible = false;
        }
      "
      width="50%"
      destroy-on-close
    >
      <DispatchConfig
        v-if="dispatchConfigVisible"
        :records="targetInfo"
        @successHandle="dispatchsuccesFn()"
        @onCancel="() => (dispatchConfigVisible = false)"
      />
    </el-dialog>
    <el-dialog
      v-if="taskLogRealTimeVisible"
      title="查看日志"
      :visible="taskLogRealTimeVisible"
      width="50%"
      :before-close="
        () => {
          taskLogRealTimeVisible = false;
        }
      "
      destroy-on-close
    >
      <TaskLogRealTime :records="targetInfo"/>
    </el-dialog>
    <el-dialog
      v-if="taskLogBatchVisible"
      title="查看日志"
      :visible="taskLogBatchVisible"
      width="50%"
      :before-close="
        () => {
          taskLogBatchVisible = false;
        }
      "
    >
      <TaskLogBatch :records="targetInfo"/>
    </el-dialog>
    <Detail ref="detail" />
    <DependencyConfig ref="dependencyConfigRef" @relationChange="relationChangeHandle"/>
    <DependencyView ref="dependencyView"/> -->
  </div>
</template>

<script lang="ts" setup>
import BaseEditForm from "./mixins/BaseEditForm";
import request from './utils/request'
import {
  drawMyOwnGraph,
  dragElTree,
  setCurrentTargetInfo,
  setTargetNameList
} from "./customGraph";
import mxgraph from './graph/VueMxGraphLoader'
import { message, Modal } from 'ant-design-vue';
import { nextTick, reactive, ref, toRefs, onActivated, defineEmits, onMounted, h, createVNode  } from "vue";
import SqlEditor from "./sql/main.vue";
import Studio from "./studio/main.vue";
import Jar from "./jarDevelopmentPage/main.vue";
// import DispatchConfig from "./queryComponents/dispatchConfig.vue";

// import Detail from "./detail.vue";
// import TaskLogRealTime from './queryComponents/taskLogRealTime.vue'
// import TaskLogBatch from './queryComponents/taskLogBatch.vue'
// import DependencyConfig from "./dependencyConfig/index.vue";
// import DependencyView from "./denpendencyView/index.vue"

const {
  mxUtils, //工具类
  mxCell, //组件单位
  mxGraphModel, //画布数据模型,由mxCell数组和其他属性组成
  mxGraph, // 画布对象,主要用作初始化画布
  mxEvent, //事件,宏观的整体的
  mxEventObject, // 事件对象,与mxEvent是所属关系
  mxCodec, // 序列化反序列化
  mxOutline,
  mxFastOrganicLayout
  //....
} = mxgraph;

// export default {
  // components: { SqlEditor, Studio, Jar, DispatchConfig, Detail, TaskLogRealTime, TaskLogBatch, DependencyConfig, DependencyView },
  // mixins: [BaseEditForm],

const state = reactive({
  taskLogRealTimeVisible: false,
  taskLogBatchVisible: false,
  haveEdited: false,
  xml: undefined,
  dispatchConfigVisible: false,
  taskNameList: {
    FlinkSQL: [],
    FlinkComponent: [],
    FlinkJar: [],
  },
  targetInfo: {},
  targetId: "",
  jsonData: {},
  flinkType: [
    { label: "FlinkSQL", value: "FlinkSQL", id: "" },
    { label: "Flink图形化开发", value: "FlinkComponent", id: "" },
    { label: "FlinkJar", value: "FlinkJar", id: "" },
  ],
  graph: undefined,
  outline: undefined,
});

const outlineRef= ref();
const sqlEditorRef = ref();
const jarRef = ref();
const studioRef = ref();
onMounted(()=> {
  let element = document.getElementById("graph");
  console.log(element);
  let graph = drawMyOwnGraph(element);

  state.outline = new mxOutline(graph, outlineRef.value);
  state.outline.setEnabled(true);
  //这样,从js中冒泡出来的delete-cell事件就被vue接收到了
  let that = this;
  graph.addListener(mxEvent.REMOVE_CELLS, function (sender, evt) {
    //sender是一个mxEventSource对象,可以用来剔除同名事件
    //evt是之前定义的mxEventObject对象,也是参数保存的位置
    if (sender === graph) {
      //判断这个事件是否来自当前画布
      let deleted = evt.getProperty("cells");
      //然后你就可以调用你的后端请求或者把这件事告诉其他的vue组件
      // this.后端post(cells)
      // this.$emit('aaa',cells)
    }
  });
  graph.addListener(mxEvent.ADD_CELLS, function (sender, evt) {
    state.haveEdited = true;
    save();
    let addCell = evt.properties.cells[0];
    if (addCell.vertex) {
      // 判断是否为组节点
      if (addCell.isGroup) {
        // message.info("添加了一个组");
      } else {
        // message.info("添加了一个节点");
      }
      //  向jsonData中更新数据
    } else if (addCell.edge) {
      request({
        url: "/flink/taskRelation/add",
        method: "post",
        data: {
          dirId: state.targetInfo.id,
          lastNodeId: addCell.source.id,
          nextNodeId: addCell.target.id,
        },
      }).then((res) => {
        addCell.setId(res.id);
      });
    }
  });

  // 开启画布平滑移动
  // this.graph.setPanning(true);
  graph.setPanning = true;
  // 开启提示
  graph.setTooltips(false);
  // 允许连线
  graph.setConnectable(true);
  //移动元素的步长
  graph.gridSize = 3;
  graph.setBorder(160);
  graph.setEnabled(true) // 设置启用,就是允不允许你改变CELL的形状内容。

  graph.popupMenuHandler.factoryMethod = function (menu, cell, evt) {
    if (cell.edge) {
      menu.addItem("删除连线", null, function () {
        Modal.confirm({
          title: '提示',
          content: '你确定要删除这条连接吗？',
          onOk() {
            request({
              url: "/flink/taskRelation/deleteById",
              method: "get",
              params: {
                id: cell.id
              },
            }).then((res) => {
              graph.removeCells([cell]);
              state.haveEdited = true;
              save();
            });
          },
          onCancel() {
            message.info('取消操作');
          },
        });
      });
    }
    if (cell.vertex) {
      menu.addItem("编辑", null, function () {
        let typeArr = cell.value.split("-");
        let type = typeArr[0];
        switch (type) {
          case "FlinkComponent":
            studioRef.value.init({
              id: cell.id,
              jobName: cell.value,
            });
            break;
          case "FlinkSQL":
            sqlEditorRef.value.init({
              id: cell.id,
              jobName: cell.value,
            });
            break;
          case "FlinkJar":
            jarRef.value.init({
              id: cell.id,
              jobName: cell.value,
            });
            break;
        }
      });
      menu.addItem("删除节点", null, function () {
        console.log(cell);
        if(cell.edges && cell.edges.length > 0) {
          message.error(`请先删除“${cell.value}”节点的输入输出连接`);
          return;
        }
        Modal.confirm({
          title: '提示',
          content: '你确定要删除这个节点吗？',
          onOk() {
            request({
              url: `/flink/taskInfo/delete?id=${cell.id}`,
              method: "delete",
            }).then((res) => {

              // graph.removeCells([cell]);
              // state.haveEdited = true;
              // deleteName(cell.value);
              // save();

              try {
                graph.cellsRemoved([cell]);
              } catch(err) {
                console.log('removeCells', err);
              } finally {
                // graph.getModel().endUpdate();
                state.haveEdited = true;
                deleteName(cell.value);
                save();
              }
            });
          },
          onCancel() {
            message.info('取消操作');
          },
        });
      });
    }
  };
  // this.$store.dispatch("setGraph", graph);
  state.graph = {...toRefs(graph)};
  initDragableElement();
  // test();
});

const test = () => {
  const parent = state.graph.getDefaultParent();

  state.graph.getModel().beginUpdate();
    try {
        const v1 = state.graph.insertVertex(parent, null, "Hello", 20, 80, 80, 30);
        v1.setConnectable(false);
        // const v11 = graph.insertVertex(v1, null, "", 1, 1, 10, 10);
        // v11.geometry.offset = new mxPoint(-5, -5);
        // v11.geometry.relative = true;
        // const v12 = graph.insertVertex(v1, null, "", 1, 0, 10, 10);
        // v12.geometry.offset = new mxPoint(-5, -5);
        // v12.geometry.relative = true;
        // const v2 = graph.insertVertex(parent, null, "World!", 200, 150, 80, 30);
        // const v3 = graph.insertVertex(parent, null, "World2", 200, 20, 80, 30);
        // const e1 = graph.insertEdge(parent, null, "", v11, v2);
        // const e2 = graph.insertEdge(parent, null, "", v12, v3);
    } finally {
        // Updates the display
        state.graph.getModel().endUpdate();
    }
}

const dependencyConfigRef = ref()
const dependencyConfigHandle = () => {
  dependencyConfigRef.value.init(state.targetInfo);
};

const dependencyViewRef = ref();
const dependencyViewHandle = () => {
  dependencyViewRef.value.init(state.targetInfo);
};

const commandHandler = (command:any) => {
  if(command == 'zoomOut') {
    state.graph.zoomOut();
  } else {
    state.graph.zoomIn();
  }
};
const init = (targetInfo:any) => {
  state.haveEdited = false;
  state.graph.removeCells(
    state.graph.getChildVertices(state.graph.getDefaultParent())
  );
  getTargetInfo(targetInfo.id);
  state.taskNameList = {
    FlinkSQL: [],
    FlinkComponent: [],
    FlinkJar: [],
  };
  return;
};
const deleteName = (name:any) => {
  let nameArr = name.split('-');
  let type = nameArr[0];
  let nameList = state.taskNameList[type];
  let index = nameList.indexOf(name);
  if (index > -1) {
    nameList.splice(index, 1);
  }
  setTargetNameList(state.taskNameList);
};
const save = () => {
  var encoder = new mxCodec();
  var node = encoder.encode(state.graph.getModel());
  let xml = mxUtils.getPrettyXml(node); //以窗口的方式展示
  console.log(xml);
  // state.graph.removeCells(
  //   state.graph.getChildVertices(state.graph.getDefaultParent())
  // );
  // let doc = mxUtils.parseXml(xml);
  // let codec = new mxCodec(doc);
  // codec.decode(doc.documentElement, state.graph.getModel());
  request({
    url: "/flink/taskDir/update",
    method: "post",
    data: {
      id: state.targetInfo.id,
      canvas:xml,
    },
  }).then((res) => {
    state.haveEdited = false;
    message.success('操作成功');
  });
};
const checkLogHandle = () => {
  if (state.targetInfo.taskScheduleType === 'realtime') {
    state.taskLogRealTimeVisible = true
  } else {
    state.taskLogBatchVisible = true
  }
};

const detailRef = ref()
const detailHandle = () => {
  detailRef.value.init(state.targetInfo);
};

const dispatchConfigHandle = () => {
  state.dispatchConfigVisible = true;
};

const dispatchsuccesFn = () => {
  state.dispatchConfigVisible = false;
  getTargetInfo(state.targetInfo.id);
};

const getTargetInfo = (id:any) => {
  request({
    url: "/flink/taskDir/getById",
    method: "get",
    params: {
      id,
    },
  }).then((res) => {
    console.log(res);
    state.targetInfo = res;
    setCurrentTargetInfo(res);
    setGraph(res.canvas);
  });
};
const setGraph = (xmlString:any) => {
  let doc = mxUtils.parseXml(xmlString);
  let codec = new mxCodec(doc);
  codec.decode(doc.documentElement, state.graph.getModel());
  let parentCell = state.graph.getModel().getCell('1');
  if(parentCell && parentCell.children) {
    parentCell.children.map(cell => {
      let name = cell.value;
      if(name) {
        if(name.indexOf('FlinkSQL') >= 0) {
          state.taskNameList.FlinkSQL.push(name);
        } else if(name.indexOf('FlinkComponent') >= 0) {
          state.taskNameList.FlinkComponent.push(name);
        } else if(name.indexOf('FlinkJar') >= 0) {
          state.taskNameList.FlinkJar.push(name);
        }
      }
    })
    console.log(state.taskNameList);
  }
  setTargetNameList(state.taskNameList)
};
const initDragableElement = () => {
  let elTypes = document.getElementsByClassName("flink-type-item");
  for (let i = 0; i < elTypes.length; i++) {
    let elType = elTypes[i];
    let data = state.flinkType[i];
    dragElTree(state.graph, elType, data, (type:any, jobName:any) => {
      state.taskNameList[type].push(jobName);
    });
  }
};
const executeOnce = () => {
  request({
    url: "/xxljob/trigger",
    method: "get",
    params: {
      dirId: state.targetInfo.id,
    },
  }).then((res) => {
    message.success(res);
  });
};
const execute = () => {
  let url = state.targetInfo.state == '1' ? 'xxljob/stop': '/xxljob/start'
  request({
    url,
    method: "get",
    params: {
      dirId: state.targetInfo.id,
    },
  }).then((res) => {
    message.success(res);
    getTargetInfo(state.targetInfo.id);
  });
};
const relationChangeHandle = () => {
  getTargetInfo(state.targetInfo.id);
}

defineExpose({
  init
});
</script>

<style lang="less">
.mxPopupMenu {
  position: absolute;
  width: 100px;
  background-color: white;
  border-radius: 5px;
  border: solid 1px rgba(0, 0, 0, .1);
  .mxPopupMenuItem {
    height: 40px;
    cursor: pointer;
  }
  .mxPopupMenuItem:hover {
    color: #3c9afb;
  }
}
</style>

<style lang="less" scoped>

.work-wrapper {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  .top-wrapper {
    height: 60px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    border-bottom: solid 1px rgba(0, 0, 0, .1);
    .title {
      margin: 0 10px;
      font-size: 16px;
    }
  }
  .content-wrapper {
    flex: 1;
    min-height: 0;
    position: relative;
    display: flex;
    .flink-type-wrapper {
      height: 100%;
      width: 160px;
      border-right: solid 1px rgba(0, 0, 0, .1);;
      box-sizing: border-box;
      .flink-type-item {
        cursor: pointer;
        height: 40px;
        background-color: #3c9afb;
        color: white;
        margin: 10px;
        border-radius: 8px;
        display: flex;
        justify-content: center;
        align-items: center;
        span {
          user-select: none;
        }
      }
    }
    .graph-wrapper {
      flex: 1;
      min-width: 0;
      height: 100%;
      position: relative;

      .zoom-wrapper {
        position: absolute;
        top: 0;
        left: 20;
        width: 120px;
        height: 60px;
        display: flex;
        flex-direction: row;
        justify-content: center;
        align-items: center;
        z-index: 999;
      }

      .outline {
        position: absolute;
        z-index: 999;
        right: 0;
        bottom: 0;
        // background-color: blue;
        border: 3px solid #ededed;
        box-shadow: 9px 9px 12px -4px #cdc7c7;
        width: 160px;
        height: 120px;
        background: rgba(255, 255, 255, 0.5);
      }
      .graph {
        width: 100%;
        height: 100%;
      }
    }

    .func-wrapper {
      position: absolute;
      right: 0;
      top: 0;
      width: 30px;
      .func-btn {
        cursor: pointer;
        margin: 10px 0;
        padding: 20px 12px;
        width: 100%;
        font-size: 16px;
        display: flex;
        justify-content: center;
        align-items: center;
        text-align: center;
        background-color: #3c9afb;
        color: white;
        border-top-left-radius: 8px;
        border-bottom-left-radius: 8px;
        writing-mode: vertical-rl; /* 从上到下，从右到左 */
        border: solid 1px rgba(0, 0, 0, .1);;
        border-right: none;
      }
    }
  }
}
</style>
